mod column_handler;
mod context_menus;
mod list_table;
mod list_table_server;
mod order_filter;
mod table_cell;
mod table_footer;
use std::collections::{HashMap, HashSet};

use chrono::{Datelike, NaiveDate};
pub use column_handler::*;
use icondata as i;
pub use list_table::*;
pub use list_table_server::*;
use serde_json::Value;
use shq_common::prelude::{
    Column, ColumnCell, ColumnCellBranch, ColumnCellLeaf, DataType, FliterItem, FliterItems,
    OrderItem, report::ParamSelected,
};
pub use table_footer::*;

use leptos::prelude::*;
use shq_common::prelude::{Direction, WhereCause};
#[derive(Clone)]
pub struct TableState {
    conf_name: StoredValue<String>,
    list_data: RwSignal<Vec<serde_json::Value>>,
    total: RwSignal<usize>,
    show_cols: RwSignal<Vec<String>>,
    order_cols: HashMap<String, RwSignal<Option<Direction>>>,
    filter_cols: HashMap<String, RwSignal<Option<WhereCause>>>,
    curr_cell: RwSignal<Option<(usize, usize)>>,
    selected_indexs: RwSignal<Vec<usize>>,
    selected_col: RwSignal<Option<(String,bool)>>,
    page: RwSignal<usize>,
    page_size: RwSignal<usize>,
    context_action_index: RwSignal<Option<usize>>,
    show_sel_cols_drawer: RwSignal<bool>,
    show_order_filter_model: RwSignal<bool>,
    curr_context_menu: RwSignal<Option<(i32, i32, String, DataType)>>,
    limit_filters: HashSet<String>,
    maximize_sub_window: RwSignal<bool>,
}
impl TableState {
    pub fn from_columns(
        rept_name: StoredValue<String>,
        cols: &[Column],
        list_data: Option<Vec<serde_json::Value>>,
    ) -> Self {
        let mut col_props: Vec<(String, bool, bool, bool, Option<FliterItems>)> = Vec::new();
        for col in cols.iter() {
            get_column_leafs_props(col, &mut col_props);
        }
        let mut shows: Vec<String> = Vec::new();
        let mut orders: HashMap<String, RwSignal<Option<Direction>>> = HashMap::new();
        let mut filters: HashMap<String, RwSignal<Option<WhereCause>>> = HashMap::new();
        let mut limit_filters: HashSet<String> = HashSet::new();
        for (name, display, filter, sorter, default_filter) in col_props.into_iter() {
            if display {
                shows.push(name.clone());
            }
            if sorter {
                orders.insert(name.clone(), RwSignal::new(None));
            }
            if let Some(fitems) = default_filter.as_ref() {
                let wc = WhereCause {
                    field_name: name.clone(),
                    filter_items: fitems.filter_items.clone(),
                    and_or: fitems.and_or.clone(),
                };
                filters.insert(name.clone(), RwSignal::new(Some(wc)));
            } else {
                filters.insert(name.clone(), RwSignal::new(None));
            }
            if !filter {
                limit_filters.insert(name.clone());
            }
        }
        TableState {
            conf_name: rept_name,
            list_data: RwSignal::new(list_data.map(|ld| ld.into()).unwrap_or(Vec::new())),
            total: RwSignal::new(0),
            show_cols: RwSignal::new(shows),
            order_cols: orders,
            filter_cols: filters,
            curr_cell: RwSignal::new(None),
            selected_col: RwSignal::new(None),
            selected_indexs: RwSignal::new(Vec::new()),
            page: RwSignal::new(1),
            page_size: RwSignal::new(20),
            show_sel_cols_drawer: RwSignal::new(false),
            show_order_filter_model: RwSignal::new(false),
            limit_filters,
            context_action_index: RwSignal::new(None),
            curr_context_menu: RwSignal::new(None),
            maximize_sub_window: RwSignal::new(false),
        }
    }
    pub fn from_column_cells(
        rept_name: StoredValue<String>,
        cols: &[ColumnCell],
        exclude_cols: &[&str],
    ) -> Self {
        let mut col_props: Vec<(&str, bool, bool, bool, Option<&FliterItems>,Option<&ParamSelected>)> = Vec::new();
        for col in cols.iter() {
            if let ColumnCell::Leaf(col) = col {
                if exclude_cols.contains(&col.name.as_str()) {
                    continue;
                }
            }
            get_column_cell_leafs_props(col, &mut col_props);
        }
        let mut shows: Vec<String> = Vec::new();
        let mut orders: HashMap<String, RwSignal<Option<Direction>>> = HashMap::new();
        let mut filters: HashMap<String, RwSignal<Option<WhereCause>>> = HashMap::new();
        let mut limit_filters: HashSet<String> = HashSet::new();
        for (name, display, filter, sorter, default_filter,param_selected) in col_props.into_iter() {
            if display {
                shows.push(name.to_owned());
            }
            if sorter {
                orders.insert(name.to_owned(), RwSignal::new(None));
            }
            if let Some(fitems) = default_filter.as_ref() {
                let wc = WhereCause {
                    field_name: name.to_owned(),
                    filter_items: fitems.filter_items.clone(),
                    and_or: fitems.and_or.clone(),
                };
                filters.insert(name.to_owned(), RwSignal::new(Some(wc)));
            } else if let Some(ps) = param_selected {
                let value = match ps.default_values {
                    shq_common::prelude::report::DefaultSelected::Single(ref item) => {
                        Some(item.clone())
                    }
                    shq_common::prelude::report::DefaultSelected::Multiple(ref items) => {
                        Some(items.join("|"))
                    }
                    shq_common::prelude::report::DefaultSelected::None => {
                        None
                    }
                };
                let fi=FliterItem {
                    operator: if ps.multiple {
                        shq_common::prelude::Operator::In
                    } else {
                        shq_common::prelude::Operator::Eq
                    },
                    value: value,
                };
                filters.insert(name.to_owned(), RwSignal::new(Some(WhereCause {
                    field_name: name.to_owned(),
                    filter_items: vec![fi],
                    and_or: shq_common::prelude::AndOr::And,
                })));
            } else {
                filters.insert(name.to_owned(), RwSignal::new(None));
            }
            if !filter {
                limit_filters.insert(name.to_owned());
            }
        }
        TableState {
            conf_name: rept_name,
            list_data: RwSignal::new(Vec::new()),
            total: RwSignal::new(0),
            show_cols: RwSignal::new(shows),
            order_cols: orders,
            filter_cols: filters,
            curr_cell: RwSignal::new(None),
            selected_col: RwSignal::new(None),
            selected_indexs: RwSignal::new(Vec::new()),
            page: RwSignal::new(1),
            page_size: RwSignal::new(20),
            show_sel_cols_drawer: RwSignal::new(false),
            show_order_filter_model: RwSignal::new(false),
            limit_filters,
            context_action_index: RwSignal::new(None),
            curr_context_menu: RwSignal::new(None),
            maximize_sub_window: RwSignal::new(false),
        }
    }
}
impl TableState {
    pub fn display(&self) -> ReadSignal<Vec<String>> {
        self.show_cols.read_only()
    }
    pub fn conf_name(&self) -> StoredValue<String> {
        self.conf_name.clone()
    }
    pub fn is_display(&self, key: &str) -> bool {
        self.show_cols.get().iter().any(|s| s == key)
    }
    pub fn add_display(&self, key: &str) {
        self.show_cols.update(|s| {
            if !s.contains(&key.to_string()) {
                s.push(key.to_string());
            }
        });
    }
    pub fn del_display(&self, key: &str) {
        self.show_cols.update(|s| {
            if let Some(pos) = s.iter().position(|s| s == key) {
                s.remove(pos);
            }
        });
    }
    fn get_col_index(&self, key: &str) -> Option<usize> {
        self.show_cols.get().iter().position(|s| s == key)
    }
    pub fn all_orders(&self) -> HashMap<String, RwSignal<Option<Direction>>> {
        self.order_cols
            .iter()
            .filter_map(|(k, v)| {
                if v.with(|v| v.is_some()) {
                    Some((k.clone(), v.clone()))
                } else {
                    None
                }
            })
            .collect()
    }
    pub fn all_filters(&self) -> HashMap<String, RwSignal<Option<WhereCause>>> {
        self.filter_cols
            .iter()
            .filter_map(|(k, v)| {
                if v.with(|v| v.is_some()) {
                    Some((k.clone(), v.clone()))
                } else {
                    None
                }
            })
            .collect()
    }
    pub fn has_order(&self) -> bool {
        self.order_cols.iter().any(|(_, o)| o.get().is_some())
    }
    pub fn has_filter(&self) -> bool {
        self.filter_cols
            .iter()
            .any(|(n, o)| o.with(|f| f.is_some()) && !self.limit_filters.contains(n))
    }
    pub fn get_orders(&self) -> Vec<OrderItem> {
        self.order_cols
            .iter()
            .filter_map(|(k, v)| v.get().map(|o| OrderItem::new(k.clone(), o)))
            .collect()
    }
    pub fn get_filters(&self) -> Vec<WhereCause> {
        self.filter_cols
            .iter()
            .filter_map(|(_, v)| v.get())
            .collect()
    }
    pub fn order(&self, key: &str) -> Option<RwSignal<Option<Direction>>> {
        self.order_cols.get(key).map(|o| o.clone())
    }
    pub fn set_init_filters(&self, filters: Vec<WhereCause>) {
        for filter in filters {
            self.filter_cols.get(&filter.field_name).map(|f| {
                f.set(Some(filter));
            });
        }
    }
    pub fn filter(&self, key: &str) -> Option<RwSignal<Option<WhereCause>>> {
        self.filter_cols.get(key).map(|f| f.clone())
    }
    pub fn list_data(&self) -> RwSignal<Vec<serde_json::Value>> {
        self.list_data.clone()
    }
    pub fn total(&self) -> RwSignal<usize> {
        self.total.clone()
    }
    pub fn set_list_data(&self, data: Vec<serde_json::Value>) {
        self.list_data.set(data);
    }
    pub fn selected_col(&self) -> RwSignal<Option<(String,bool)>> {
        self.selected_col
    }
    pub fn set_selected_col(&self, val: Option<(String,bool)>) {
        self.selected_col.set(val);
    }
    pub fn selected_indexs(&self) -> RwSignal<Vec<usize>> {
        self.selected_indexs
    }
    pub fn get_curr_row(&self) -> Option<serde_json::Value> {
        self.selected_indexs.with(|s| {
            if s.len() == 1 {
                self.list_data.with_untracked(|ld| ld.get(s[0]).cloned())
            } else if s.len() > 1 {
                self.list_data
                    .with_untracked(|ld| ld.get(s[s.len() - 1]).cloned())
            } else {
                // self.curr_cell().with_untracked(|c| {
                //     log::info!("选中单元格:{:?}",c);
                //     c.as_ref()
                //         .and_then(|(r, _)| self.list_data.with_untracked(|ld| ld.get(*r).cloned()))
                // })
                None
            }
        })
    }
    pub fn set_selected_indexs(&self, val: Vec<usize>) {
        self.selected_indexs.set(val);
    }
    pub fn set_selected_all(&self, val: bool) {
        self.selected_indexs.set(if val {
            (0..self.list_data.with(|d| d.len())).collect()
        } else {
            Vec::new()
        });
    }
    pub fn is_selected_all(&self) -> bool {
        self.selected_indexs.with(|s| {
            if s.is_empty() {
                return false;
            }
            let len = self.list_data.with(|d| d.len());
            s.len() == len
        })
    }
    pub fn curr_cell(&self) -> RwSignal<Option<(usize, usize)>> {
        self.curr_cell
    }
    pub fn set_curr_cell(&self, cell: Option<(usize, usize)>) {
        self.curr_cell.set(cell);
    }
    pub fn set_display(&self, cols: Vec<String>) {
        self.show_cols.set(cols);
    }
    pub fn set_order(&self, key: &str, value: Option<Direction>) {
        self.order_cols.get(key).map(|o| o.set(value));
    }
    pub fn set_filter(&self, key: &str, value: Option<WhereCause>) {
        self.filter_cols.get(key).map(|f| f.set(value));
    }
    pub fn set_page(&self, page: usize) {
        self.page.set(page);
    }
    pub fn set_page_size(&self, page_size: usize) {
        self.page_size.set(page_size);
    }
    pub fn page(&self) -> RwSignal<usize> {
        self.page
    }
    pub fn page_size(&self) -> RwSignal<usize> {
        self.page_size
    }
    pub fn show_sel_cols_drawer(&self) -> RwSignal<bool> {
        self.show_sel_cols_drawer
    }
    pub fn show_order_filter_model(&self) -> RwSignal<bool> {
        self.show_order_filter_model
    }
    pub fn set_show_sel_cols_drawer(&self, val: bool) {
        self.show_sel_cols_drawer.set(val);
    }
    pub fn set_show_order_filter_model(&self, val: bool) {
        self.show_order_filter_model.set(val);
    }
    pub fn curr_context_menu(&self) -> RwSignal<Option<(i32, i32, String, DataType)>> {
        self.curr_context_menu
    }
    pub(super) fn clear_all_orders(&self) {
        self.order_cols.iter().for_each(|(_, c)| c.set(None));
    }
    pub(super) fn clear_all_filters(&self) {
        self.filter_cols.iter().for_each(|(n, c)| {
            if !self.limit_filters.contains(n) {
                c.set(None)
            }
        });
    }
    pub fn maximize_sub_window(&self) -> RwSignal<bool> {
        self.maximize_sub_window
    }
    pub(super) fn has_icon(&self, key: &str) -> bool {
        self.filter_cols
            .get(key)
            .map(|fs| fs.with_untracked(|f| f.is_some()))
            .is_some()
            || self
                .order_cols
                .get(key)
                .map(|o| o.get_untracked())
                .flatten()
                .is_some()
    }
    pub(super) fn get_icon(&self, key: &str) -> Option<i::Icon> {
        let is_filter = self.filter_cols.get(key).map(|fs| fs.with(|f| f.is_some()));
        let order = self.order_cols.get(key).map(|o| o.get()).flatten();
        let is_filter = is_filter.unwrap_or(false);
        Some(if !is_filter {
            if order.is_none() {
                i::AiDownOutlined
            } else if order == Some(Direction::Asc) {
                i::FiArrowUpCircle
            } else {
                i::FiArrowDownCircle
            }
        } else {
            if order.is_none() {
                i::BiFilterAltRegular
            } else if order == Some(Direction::Asc) {
                i::TbFilterUp
            } else {
                i::TbFilterDown
            }
        })
    }
    pub fn context_action_index(&self) -> RwSignal<Option<usize>> {
        self.context_action_index
    }
}

pub(super) fn get_column_leafs_count(col: &Column, col_state: &TableState) -> usize {
    if !is_display_column(col, col_state) {
        return 0;
    }
    let mut count = 0_usize;
    if col.children.is_empty() {
        return 1;
    }
    for ccol in col.children.iter() {
        count += get_column_leafs_count(ccol, col_state);
    }
    count
}
fn get_column_leafs_props<'a>(
    col: &'a Column,
    leafs: &mut Vec<(String, bool, bool, bool, Option<FliterItems>)>,
) {
    if col.children.is_empty() {
        let mut display = col.list_props.as_ref().map(|p| p.display).unwrap_or(false);
        let filter = col.list_props.as_ref().map(|p| p.filter).unwrap_or(false);
        let sorter = col.list_props.as_ref().map(|p| p.sorter).unwrap_or(false);
        if col.title.is_none() {
            display = true;
        }
        let mut default_filter = col.default_filter.clone();
        if let Some(ep) = col.edit_props.as_ref() {
            if ep.is_show_in_toolbar {
                if let Some(init_val) = &ep.init_val {
                    let operator = if ep.options.is_some() {
                        shq_common::prelude::Operator::In
                    } else {
                        shq_common::prelude::Operator::Eq
                    };
                    let fi = FliterItem {
                        operator,
                        value: Some(init_val.to_string()),
                    };
                    default_filter = Some(FliterItems {
                        filter_items: vec![fi],
                        and_or: shq_common::prelude::AndOr::And,
                    });
                }
            }
        }
        let name = col
            .name
            .as_ref()
            .map(|n| n.clone())
            .unwrap_or("".to_string());
        leafs.push((name, display, filter, sorter, default_filter));
        return;
    }
    for ccol in col.children.iter() {
        get_column_leafs_props(ccol, leafs);
    }
}
fn get_column_cell_leafs_props<'a>(
    column_cell: &'a ColumnCell,
    leafs: &mut Vec<(&'a str, bool, bool, bool, Option<&'a FliterItems>,Option<&'a ParamSelected>)>,
) {
    match column_cell {
        ColumnCell::Leaf(leaf) => {
            let mut display = leaf.list_props.as_ref().map(|p| p.display).unwrap_or(false);
            let filter = leaf.list_props.as_ref().map(|p| p.filter).unwrap_or(false);
            let sorter = leaf.list_props.as_ref().map(|p| p.sorter).unwrap_or(false);
            let name = leaf.name.as_str();
            let default_filter = leaf.default_filter.as_ref();
            let param_selected = leaf.param_selected.as_ref();
            if leaf.front_must {
                display = true;
            }
            leafs.push((name, display, filter, sorter, default_filter, param_selected));
        }
        ColumnCell::Branch(branch) => {
            for ccol in branch.children.iter() {
                get_column_cell_leafs_props(ccol, leafs);
            }
        }
        _ => {}
    }
}
pub(super) fn is_display_column(col: &Column, col_state: &TableState) -> bool {
    if col.children.is_empty() {
        if let Some(name) = col.name.as_ref() {
            return col_state.is_display(name);
        }
    }
    for ccol in col.children.iter() {
        let is_display = is_display_column(ccol, col_state);
        if is_display {
            return is_display;
        }
    }
    false
}
pub(super) fn get_column_deep(col: &Column, col_state: &TableState) -> u8 {
    if !is_display_column(col, col_state) {
        return 0;
    }
    if col.children.is_empty() {
        return 1;
    }
    let mut deep = 0_u8;
    for ccol in col.children.iter() {
        let d2 = get_column_deep(ccol, col_state);
        if d2 > deep {
            deep = d2;
        }
    }
    deep + 1
}
pub(super) fn get_header_cells(
    col: &Column,
    row_number: usize,
    deep: u8,
    max_deep: u8,
    cells: &mut Vec<Vec<ColumnCell>>,
    leafs: &mut Vec<(usize, usize)>,
    col_state: &TableState,
) {
    let leafs_count = get_column_leafs_count(col, col_state);
    if leafs_count == 0 {
        return;
    }
    let mut rowspan = 1_u8;
    let is_leaf = col.children.is_empty();
    if is_leaf && deep < max_deep {
        rowspan += max_deep - deep;
    }
    let cell = if col.children.len() > 0 {
        ColumnCell::Branch(ColumnCellBranch {
            title: col.title.clone().unwrap_or("".to_string()),
            children: Vec::new(),
            colspan: leafs_count as u8,
            rowspan,
        })
    } else {
        let mut unit: f64 = col.unit.to_u32() as f64;
        if let Some(fmt) = col.list_props.as_ref().map(|p| p.format.as_ref()).flatten() {
            if fmt.ends_with('%') {
                unit = unit / 100.0;
            }
        }
        ColumnCell::Leaf(ColumnCellLeaf {
            name: col.name.clone().unwrap_or("".to_string()),
            title: col.title.clone(),
            colspan: leafs_count as u8,
            rowspan,
            data_type: col.data_type.clone(),
            list_props: col.list_props.clone(),
            dict_col_name: col.dict_col_name.clone(),
            front_must: false,
            is_null: col.is_null,
            default_filter: col.default_filter.clone(),
            param_selected: None,
        })
    };
    cells[row_number].push(cell);
    for i in 1..rowspan {
        cells[row_number + i as usize].push(ColumnCell::None);
    }
    if is_leaf {
        leafs.push((row_number, cells[row_number].len() - 1));
    }
    for ccol in col.children.iter() {
        get_header_cells(
            ccol,
            row_number + 1,
            deep,
            max_deep,
            cells,
            leafs,
            col_state,
        );
    }
}
pub fn validate_value(
    data_type: DataType,
    required: bool,
    range_rule: Option<(isize, isize)>,
    value: &Value,
) -> Option<String> {
    if required && value.is_null() {
        return Some("不能为空".to_string());
    }
    if let Some((min, max)) = range_rule {
        match value {
            Value::Number(number) => {
                if number.is_f64() {
                    let fv = number.as_f64().unwrap_or(0.0);
                    if fv < min as f64 {
                        return Some(format!("值不能小于{}", min));
                    }
                    if fv > max as f64 {
                        return Some(format!("值不能大于{}", max));
                    }
                } else if number.is_i64() {
                    let iv = number.as_i64().unwrap_or(0);
                    if iv < min as i64 {
                        return Some(format!("值不能小于{}", min));
                    }
                    if iv > max as i64 {
                        return Some(format!("值不能大于{}", max));
                    }
                }
            }
            Value::String(sv) => match data_type {
                DataType::String => {
                    if sv.len() < min as usize {
                        return Some(format!("字数不能小于{}", min));
                    }
                }
                DataType::Date | DataType::Datetime => {
                    let fmt = if data_type == DataType::Date {
                        "%Y-%m-%d"
                    } else {
                        "%Y-%m-%d %H:%M:%S"
                    };
                    if let Ok(date) = NaiveDate::parse_from_str(sv, fmt) {
                        let year = date.year();
                        if year < min as i32 {
                            return Some(format!("年份不能小于{}", min));
                        }
                        if year > max as i32 {
                            return Some(format!("年份不能大于{}", max));
                        }
                    } else {
                        return Some("日期格式不正确".to_string());
                    }
                }
                _ => {}
            },
            _ => {}
        }
    }
    None
}
